import time
import uuid
import secrets
from datetime import datetime

from django.conf import settings
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
from dotenv import load_dotenv   # 需安装:pip install cryptography python-dotenv requests
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework import serializers
# from ...models import OrderNew  # V3版本的订单表
from app01 import models
# 需要安装:pip install wechatpayv3
from wechatpayv3 import WeChatPay, WeChatPayType  # WeChatPayException


wx_pay = WeChatPay(
    cert_serial_no=settings.WECHAT_SERIAL_NO,  # 商户序列号
    private_key=settings.WECHAT_PRIVATE_KEY,  # 商户私钥
    cert_dir=settings.CERT_DIR,  # 证书存放目录
    mchid=settings.WECHAT_MCHID,  # 商户号
    appid=settings.WECHAT_APPID,  # 应用id
    apiv3_key=settings.WECHAT_API3_KEY,  # 商户APIv3密钥
    wechatpay_type=WeChatPayType.MINIPROG,  # 微信支付类型
    notify_url=settings.WECHAT_NOTIFY_URL+'paymentV3/callback/'  # 支付结果回调地址
)


class PaymentSerializer(serializers.Serializer):  # 序列化器
    goodsId = serializers.CharField()
    # total = serializers.DecimalField(max_digits=10, decimal_places=2)


class PaymentV3View(APIView):
    def post(self, request):
        serializer = PaymentSerializer(data=request.data)
        if serializer.is_valid():
            goods_id = int(serializer.validated_data['goodsId'])
            # total = int(serializer.validated_data['total'] * 100)  # 转换为分
            # goods_id = request.data.get('goodsId')
            good_obj = models.Goods.objects.filter(id=goods_id).first()  # 里面有价格price
            total = good_obj.price  # 要付款的总金额(以下必须要的)
            order_no = str(uuid.uuid4())[:20]  # +时间11位 商户系统内部订单号(以下必须要的,最大32位)
            # 用户对象.模拟的是1
            user_obj = models.UserInfo.objects.filter(id=1).first()  # 里面有openid
            openid = user_obj.openid
            # 在订单表中增加订单记录, user_id是登录客户的id,暂时定死了为1; uid为订单号,暂时定为str(uuid.uuid4())
            # order_obj = models.Order.objects.create(goods=good_obj, user=user_obj, uid=order_no, status=1)

            try:
                # 创建微信支付订单
                out_trade_no = f"{order_no}_{int(time.time())}"
                code, message = wx_pay.pay(
                    description='测试',
                    out_trade_no=out_trade_no,
                    amount={'total': total, 'currency': 'CNY'},
                    payer={'openid': openid},
                    notify_url=settings.WECHAT_NOTIFY_URL+'paymentV3/callback/',
                    # pay_type=WeChatPayType.JSAPI
                )
                if code in [200, 201]:
                    models.OrderNew.objects.create(
                        order_no=out_trade_no,
                        user=user_obj,
                        total=total / 100,
                        status=0
                    )
                    # 生成 timeStamp（时间戳）
                    timeStamp = str(int(time.time()))  # 秒级时间戳，转为字符串
                    # 生成 nonceStr（随机字符串）
                    nonce_str = secrets.token_hex(16)  # 32 位随机字符串（16字节的hex）

                    # 生成 paySign（支付签名）
                    load_dotenv()
                    # 从settings中读取私钥内容
                    private_key_str = settings.WECHAT_PRIVATE_KEY
                    private_key = serialization.load_pem_private_key(
                        private_key_str.encode(),
                        password=None,
                        backend=default_backend()
                    )
                    # 构造签名串
                    import ast
                    message_data = ast.literal_eval(message)
                    prepay_id = message_data['prepay_id']  # 不能用.取值!!!
                    sign_str = f"{settings.WECHAT_APPID}\n{timeStamp}\n{nonce_str}\nprepay_id={prepay_id}\n"

                    # 使用 SHA256-RSA 签名
                    from cryptography.hazmat.primitives import hashes
                    from cryptography.hazmat.primitives.asymmetric import padding
                    signature = private_key.sign(
                        sign_str.encode(),
                        padding.PKCS1v15(),
                        hashes.SHA256()
                    )
                    # Base64 编码签名结果
                    import base64
                    pay_sign = base64.b64encode(signature).decode()

                    message_data['timeStamp'] = timeStamp
                    message_data['nonce_str'] = nonce_str
                    message_data['pay_sign'] = pay_sign
                    return Response(message_data, status=status.HTTP_200_OK)
                else:
                    return Response({'error': message}, status=status.HTTP_400_BAD_REQUEST)
            except Exception as e:
                return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class PaymentCallbackView(APIView):
    """ 支付成功由腾讯发来通知,根据返回结果修改订单状态 """
    def post(self, request):
        try:
            data = wx_pay.callback(request.headers, request.body)
            if data and data.get('event_type') == 'TRANSACTION.SUCCESS':
                # print('V3版本data:', data)  # 里面返回来非常多的数据,需要查了用
                order = models.OrderNew.objects.get(order_no=data['resource']['out_trade_no'])

                order.status = 1
                order.transaction_id = data['resource']['transaction_id']
                order.pay_time = data['resource']['success_time']
                order.save()
                print('V3版本--支付成功!', datetime.today())
                return Response({'code': 'SUCCESS'})
            print('注意:V3版本--支付失败了!!!', datetime.today())
            return Response({'code': 'FAIL', 'message': 'Invalid callback'})
        except Exception as e:
            print('注意:V3版本--支付代码里有错误!!!', datetime.today())
            return Response({'code': 'FAIL', 'message': str(e)})


# # ‌退款实现要点‌：
# class RefundOrder(APIView):
#     def post(self, request):
#         # 必须校验用户权限和订单归属
#         if not request.user.has_perm('order.refund'):
#             return Response(status=403)
#
#         # 调用微信退款API
#         wechatpay.request("POST", "refund/domestic/refunds", data)
